// 元编程
// 函数名 - week3 function
// 元属性
// new.target -> new是虚拟上下文

class Parent {
    constructor() {
        if (new.target === Parent) {
            console.log('Parent init');
        } else {
            console.log('Other init');
        }
    }
}

class Child extends Parent {}
const a = new Parent(); // -> Parent init
const b = new Child(); // -> Other init

// Symbol
// Proxy
// 1. 创建
// const target = { id: 'target' }
// const handler = {};
// const proxy = new Proxy(target, handler);
// target.id = 'foo'
// console.log(proxy.id) // foo

// 捕获器 trap
// const target = { id: 'get' }
// const handler = {
//     get(trapTarget, property, receiver) {
//         return trapTarget[property]
//     }
// }
// const proxy = new Proxy(target, handler);
// target.id // get
// proxy.id // get

// proxy[property], proxy.property, Object.create(proxy)[property]

const target = { id: 'get' }
const handler = {
    get: Reflect.get
}
const proxy = new Proxy(target, handler);
const proxy1 = new Proxy(target, Reflect) // 最简单直接的
target.id // get
proxy.id // get

// 捕获器不变式
const target = {}
Object.defineProperty(target, 'foo', {
    configurable: false,
    writable: false,
    value: 'foo'
});
const handler = {
    get() {
        return 'bar'
    }
}
const proxy = new Proxy(target, handler);
proxy.foo // TypeError

// 可撤销代理
const target = { foo: 'bar' }
const handler = { get() { return 'intercepted' } }
const { proxy, revoke } = Proxy.revocable(targer, handler);
target.foo // bar
proxy.foo // intercepted
revoke();
proxy.foo // TypeError

// Reflect & Object
const o = {}
try {
    Object.defineProperty(o, 'foo', 'bar');
    console.log('succ');
} catch(e) {
    console.log('fail');
}

if (Reflect.defineProperty(o, 'foo', { value: 'bar' })) {
    console.log('succ');
} else {
    console.log('fail');
}

// 提供状态标记
Reflect.defineProperty()
Reflect.preventExtensions()
Reflect.setPrototypeOf()
Reflect.set()
Reflect.defineProperty()
// 一等函数代替操作符
Reflect.get() // -> 对象访问的操作符
Reflect.set() // ->  =
Reflect.has() // -> in / with()
Reflect.deleteProperty() // -> delete
Reflect.construct() // -> new
// 安全的应用函数
Function.prototype.apply.call(fn, val, arguments);
Reflect.apply(fn, val, arguments);

// 代理另一个代理
const target = { foo: 'bar' }
const first = new Proxy(target, {
    get() { console.log('first'); return Reflect.get(arguments); }
});

const second = new Proxy(first, {
    get() { console.log('second'); return Reflect.get(arguments); }
})

second.foo // second, first, bar

// 问题
// 1.this
const target = {
    thisValEqualProxy() {
        return this === proxy
    }
}
const proxy = new Proxy(target, {});
target.thisValEqualProxy(); // false
proxy.thisValEqualProxy(); // true

const wm = new WeakMap();
class User {
    constructor(id) { wm.set(this, id); }
    set id(id) { wm.set(this, id); }
    get id () { return wm.get(this); }
}
const user = new User(123);
user.id // 123
// const userProxy = new Proxy(user, {});
// userProxy.id // undefined

const UserProxy = new Proxy(User, {});
const userProxy = new UserProxy(456);
userProxy.id // 456

// 代理与内部槽位 -> Date(依赖[[NumberDate]])
const target = new Date()
const proxy = new Proxy(target, {});
proxy instanceof Date // true;
proxy.getDate() // TypeError: this is not a date Object

// 代理捕获器
// get -> Reflect.get()
// set -> Reflect.set()
const handler = {
    set(target, prop, value, receiver) {}
}
// has -> Reflect.has()
const target = {}
const proxy = new Proxy(target, {
    has(target, prop) {
        return Reflect.has(...arguments)
    }
});

foo in proxy
// defineProperty -> Reflect.defineProperty()
// 返回boolean
// target, prop, desc

// getOwnPropertyDescriptor() -> Object.getOwnPropertyDescriptor()
// 返回对象
// target, prop

// deleteProperty -> Reflect.deleteProperty()
// 返回boolean
// target, prop

// ownKeys -> Reflect.ownKeys();
// 返回包含string/symbols的可枚举对象
// target

// getPrototypeOf -> Reflect.getPrototypeOf();
// 返回对象/null
// 调用时机: proxy.__proto__/Object.prototype.isPrototypeof(proxy)/proxy instanceof Object
// target

// setPrototypeOf -> Reflect.setPrototypeOf();
// 返回boolean
//  target, prototype

// isExtensible -> Reflect.isExtensible() 是否可扩展
// 返回boolean
// target

// preventExtensible -> 判断是否已经不可扩展
// 返回boolean
// target

// apply -> Reflect.apply()
// target(必须是个函数对象), this, arguments
// 调用时机： Function.prototype.apply/call

 // construct -> Reflect.construct()
 // target(必须可以用作构造函数)

 // 代理模式
 // 跟踪属性访问
 // 隐藏
const hiddenList = ['foo', 'bar']
const target = {
    foo: 1,
    bar: 2,
    baz: 3
}
const proxy = new Proxy(target, {
    get (target, prop) {
        if (hiddenList.includes(prop)) {
            return undefined
        }
        return Reflect.get(...arguments)
    },
    has (target, prop) {
        if (hiddenList.includes(prop)) {
            return false
        }
        return Reflect.get(...arguments)
    }
})
// 属性验证
// 函数和构造函数参数验证
// 数据绑定 可观察对象
const userList = []
class User {
    constructor(id) { this.id = id; }
}
const proxy = new Proxy(User, {
    construct() {
        const newUser = Reflect.construct(...arguments);
        userList.push(newUser);
        return newUser
    }
})

// 特性测试
if (!Number.isNaN) {
    Number.isNaN = function (x) {
        return x !== x
    }
}

try {
    // a = () => {} bad!
    // good
    new Function("(() => {})");
    ARROW_FUNCS_ENABLED = true
} catch (e) {
    ARROW_FUNCS_ENABLED = false
}
// 分批发布 split delivery

// FeatureTest.io https://featuretests.io

// 尾递归调用
"use strict"
// function foo (x) {
//     return x * 2
// }

// function bar (x) {
//     return 1 + foo(x) // 不是尾调用
// }

// function bar (x) {
//     x = x + 1
//     if (x > 10) {
//         return foo(x)
//     }
//     return bar (x + 1)
// }

// bar (10)

// function foo (x) {
//     if (x <= 1) {
//         return x
//     }
//     return x / 2 + foo (x - 1)
// }
// foo(123456) // RangeError

// var foo = (function (x) {
//     function _foo(acc, x) {
//         if (x <= 1) { return acc }
//         return _foo((x / 2) + acc, x -1 )
//     }
//     return function (x) {
//         return _foo(1, x)
//     }
// })()

// foo(123456)

// function trampoline(res) {
//     while(typeof res === 'function') {
//         res = res()
//     }
//     return res;
// }

// var foo = (function () {
//     function _foo(acc, x) {
//         if (x <= 1) { return acc }
//         return function partial() {
//             return _foo((x / 2) + acc, x - 1)
//         }
//     }
//     return function(x) {
//         return trampoline(_foo(1, x))
//     }
// })()

// function foo (x) {
//     let acc = 1;
//     while (x > 1) {
//         acc = (x / 2) + acc;
//         x = x - 1;
//     }
//     return acc;
// }

// try {
//     (function (x) {
//         if (x < 5E5) return foo (x + 1)
//     })(1)
//     TCO_ENABLE = true;
// } catch (e) {
//     TCO_ENABLE = false;
// }

function foo (x) {
    function _foo() {
        if (x > 1) {
            acc = acc + (x / 2)
            x = x - 1;
            return _foo();
        }
    }
    let acc = 1;
    while (x > 1) {
        try {
            _foo();
        } catch (e) {}
        return acc;
    }
}

foo(123456)
